Contribute

We are open to contributions and collaborations to improve and expand WOLF to other sensors and other ways of processing data.

Our code is located in an internal gitlab server. If you or your institution is interested in developing WOLF, we can give you permissions. Please, contact us at:

  wolf@mylist.upc.edu

Below, you will find some guidelines for contibutors:

Format and code style

In WOLF we follow the ROS C++ style guide modified to 4 spaces identation. If you develop new code for WOLF we strongly recommend that you follow that StyleGuide.

We use clang-format to automatically format our code and distribute the .clang-format file in all our repositories.

Additionally, a continuous-integration routine auto-format all the commited code.

const-correctness

As you should already know, in WOLF almost everything is stored and passed as shared pointers. This do not fits well with “const-correctness”.

Imagine the case of a class with a member which is a shared pointer. This class could have a method that even if declared const, could modify the pointed object, since the member (the pointer) is not changed. While this code would compile, this is not allowed in WOLF because it breaks the const-correctness philosofy.

Example

It woudn’t have sense that a const method in a frame could change its captures, features and factors.

In WOLF we keep the const-correctness philosofy taking care of returning const pointers in const methods. And also taking const pointers as parameters if possible. See, for example, this getters of the FrameBase:

TrajectoryBaseConstPtr getTrajectory() const;
TrajectoryBasePtr      getTrajectory();

FrameBaseConstPtr getPreviousFrame(const unsigned int& i = 1) const;
FrameBaseConstPtr getNextFrame(const unsigned int& i = 1) const;
FrameBasePtr      getPreviousFrame(const unsigned int& i = 1);
FrameBasePtr      getNextFrame(const unsigned int& i = 1);

CaptureBaseConstPtrList getCaptureList() const;
CaptureBasePtrList      getCaptureList();
bool                    hasCapture(const CaptureBaseConstPtr& _capture) const;

FactorBaseConstPtrList getFactorList() const;
FactorBasePtrList      getFactorList();
void                   getFactorList(FactorBaseConstPtrList& _fac_list) const;
void                   getFactorList(FactorBasePtrList& _fac_list);

Unit tests & Continuous Integration

WOLF plugins include several unit tests for checking the correct function of all classes. This is useful when implementing a new class, but also very important in a such connected software ecosystem. This way, when refactoring code, we are able to see which classes are affected and need to be fixed.

The continuous-integration routine, after a new commit, checks that all tests still pass for the plugin being developed and also for the plugins depending on it.

Attention

We strongly recommend to implement unit tests for the new classes and/or methods.

How to do a unit test

  1. Create a new test file in the folder test: test/gtest_my_new_class.cpp. You can start with the test/gtest_example.cpp as a template.

  2. Include the header of the class you want to test and others that you may require.

  3. Create a test for each method of the class, including constructors.

  4. Add it in the CMakeLists.txt file in the test folder.

Important

Make your test as exhaustive as you can. Tests to verify that your code:

  • works as expected with proper inputs.

  • throws errors/exceptions when receiving wrong inputs.

Apart from the gtest_my_new_class.cpp file, you may need to create other files in the test folder:

  • test/dummy: For dummy derived classes. If you implemented an abstract class, you may want to create a dummy derived class to test it.

  • test/yaml: For YAML configuration files if your test uses any (most probably it should).

  • test/data: For data files. If your test requires any data files, you can place them here.

The following is the example test gtest_example.cpp provided in all plugins. There you will find the two main ways of performing tests in gtest (TEST and TEST_F), and an example of using the custom WOLF macros for testing matrices and vectors.

#include "core/utils/utils_gtest.h"
#include "core/common/wolf.h"

int    global_param = 1;
double eps          = 1e-6;

class ExampleTest : public testing::Test
{
  public:
    double           param_1;
    bool             param_2;
    int              param_3;
    std::vector<int> param_4;

    void SetUp() override
    {
        WOLF_DEBUG("starting setup...");

        param_1 = 12.5;
        param_2 = true;
        param_3 = 42;
        param_4 = {1, 2, 3, 4, 5};

        WOLF_DEBUG("Ready!");
    }

    void TearDown() override
    {
        WOLF_DEBUG("Tearing down...");
        // This is useful if your test has outputs (files) that must be deleted or modified.
        WOLF_DEBUG("Bye!");
    }
};

// This test has access to an ExampleTest object
TEST_F(ExampleTest, test_f_example)
{
    // SetUp() has already been called

    EXPECT_EQ(param_1, 12.5);  // EXPECT: If fails, it continues (the test will fail anyway).
    EXPECT_TRUE(param_2);
    ASSERT_EQ(param_3, 42);  // ASSERT: If fails, it stops the test.
    ASSERT_EQ(param_4.size(), 5);

    // TearDown() will be called now
}

// This test does not have access to an ExampleTest object, but to the global variable
TEST(TestTest, test_example)
{
    EXPECT_FALSE(false);

    ASSERT_TRUE(true);

    ASSERT_EQ(global_param, 1);

    global_param += 2;
}

TEST(TestTest, test_example2)
{
    // The global variables can be used in any test, but they are not reset between tests.
    ASSERT_EQ(global_param, 3);

    // USEFUL CUSTOM EXPECT/ASSERTS
    // Eigen::Matrix/Vector
    EXPECT_MATRIX_APPROX(Eigen::MatrixXd::Zero(3, 1), Eigen::Vector3d::Zero(), eps);

    // Eigen::Quaterniond
    EXPECT_QUATERNION_APPROX(
        Eigen::Quaterniond::Identity(), Eigen::Quaterniond((Eigen::Vector4d() << 0, 0, 0, 1).finished()), eps);

    // Eigen::Vector corresponding to quaternion values
    EXPECT_QUATERNION_VECTOR_APPROX(
        Eigen::Quaterniond::Identity().coeffs(), (Eigen::Vector4d() << 0, 0, 0, 1).finished(), eps);

    // Eigen::Vector corresponding to pose2d values
    EXPECT_POSE2d_APPROX((Eigen::Vector3d() << 0, 0, 0).finished(), Eigen::Vector3d::Zero(), eps);

    // Eigen::Vector corresponding to pose3d values
    EXPECT_POSE3d_APPROX(
        (Eigen::Vector7d() << 0, 0, 0, 0, 0, 0, 1).finished(),
        (Eigen::Vector7d() << Eigen::Vector3d::Zero(), Eigen::Quaterniond::Identity().coeffs()).finished(),
        eps);
}

int main(int argc, char **argv)
{
    testing::InitGoogleTest(&argc, argv);
    //::testing::GTEST_FLAG(filter) = "ExampleTest.test_f_example"; // Test only this one
    //::testing::GTEST_FLAG(filter) = "TestTest.*"; // Test only the tests in this group
    return RUN_ALL_TESTS();
}

See also

Visit the GoogleTest docmentation page for more information.